.libPaths(new = "libR/")
library(VennDiagram)
library(venneuler)
library(RColorBrewer)
library(clusterProfiler)
library(GO.db)
library(enrichplot)
library(tidyverse)

Busco data

Load busco results:

acutogordius_hic <- read_tsv("../results/busco545-Nematomorpha/out_metazoa_odb10/Acutogordius_australiensis-152393-HiC.fa/run_metazoa_odb10/full_table.tsv", skip = 2)
nectonema_draft <- read_tsv("../results/busco545-Nematomorpha/out_metazoa_odb10/Nectonema_munidae-DNA05827-Draft.fa/run_metazoa_odb10/full_table.tsv", skip = 2)

Join all in one data frame:

buscos <- bind_rows("Acutogordius_HiC" = acutogordius_hic,
                    "Nectonema_Draft" = nectonema_draft,
                    .id = "assembly") %>% rename("BuscoID" = "# Busco id")
buscos

Set of all 954 Metazoa buscos:

metazoa_genes <- buscos %>% pull(BuscoID) %>% unique()
#write_csv2(as.data.frame(metazoa_genes),
#           file = "../data/metazoa_buscos.csv", col_names = F)

Exploratory plots

Length histogram:

ggplot(buscos) +
  geom_histogram(aes(x = Length, fill = Status)) +
  scale_fill_brewer(type = "qual") +
  theme_minimal() +
  facet_wrap(~assembly, ncol=1)

Length boxplot:

ggplot(buscos) +
  geom_boxplot(aes(x = Status, y = Length, fill = assembly)) +
  scale_fill_brewer(type = "qual") +
  theme_minimal()

Venn diagram

List of genes present for each sample, and the total metazoan set:

present <- buscos %>%
  filter(Status != "Missing") %>%
  with(., split(BuscoID, assembly))

present$Metazoa_buscos <- metazoa_genes

summary(present)
                 Length Class  Mode     
Acutogordius_HiC 699    -none- character
Nectonema_Draft  663    -none- character
Metazoa_buscos   954    -none- character

Only option that automatically puts smaller sets inside larger set, but circles not proportional:

myCol <- brewer.pal(3, "Pastel2")
vennbuscos <- venn.diagram(x = present, disable.logging = T,
                           filename = '../figures/venn_buscos/venn_buscos.png',
             category.names = c("Acutogordius" , "Nectonema" , "Metazoa BUSCOS"),
             imagetype="png", resolution = 300,
             height = 480, width = 480,
             # Circles
             lwd = 2, lty = 'blank', fill = myCol,
             # Numbers
             cex = .5, fontface = "bold", fontfamily = "sans",
             # Set names
             cat.cex = 0.4, cat.fontface = "bold", cat.default.pos = "text",
             cat.fontfamily = "sans")

Option with correct proportions, but not nested: This is the one used for the main Figure in the manuscript.

pdf(file = "../figures/venn_buscos/venn_prop.pdf", width = 5, height = 5)
  plot(venneuler(c(Acutogordius=78, Nectonema=88, MetazoaBuscos=954,
                 "Acutogordius&Nectonema"=568, "Acutogordius&MetazoaBuscos"=78,
                 "Nectonema&MetazoaBuscos"=88 ,"Acutogordius&Nectonema&MetazoaBuscos"=734)))
dev.off()

Chi-square test for overlap of 220 missing genes. First create a matrix with number of genes for each/both species, taken easily from the venn_buscos.png file created just above.

# cols = present in Acuto, absent in Acuto
# rows = present in Necto, absent in Necto
chimatrix <- matrix(c(568, 88, 78, 220), ncol = 2)
chimatrix
     [,1] [,2]
[1,]  568   78
[2,]   88  220
chisq.test(chimatrix)

    Pearson's Chi-squared test with Yates' continuity correction

data:  chimatrix
X-squared = 339.31, df = 1, p-value < 2.2e-16

Enrichment analysis

Hypergeometric enrichment (= simple enrichment or over-representation analysis) to assess whether the number of missing buscos associated with a given function is larger than expected by chance.

1- Define GO universe, the background set of all GO terms against which we will test for enrichment. It should include any gene that could have been positive (>GO terms of all 954 metazoan buscos and their GO ancestral terms) 2- Define genes significant for variable of interest (>missing buscos in genome of nematomorphs) 3- Calculate proportion of significant genes in gene set 4- Estimate probability and significance (p-value)

GO universe

Start with loading the data we got with a custom python script (buscos_to_GO.py) that retrieves GO ids/terms from the OrthoDB v10 based on the list of all 954 Metazoan Busco IDs. Then filter only for GO terms (there are interpro and other lines there too). The terms themselves were not complete (NA for unclassified GOs), and even those do have classification if you look them up in https://www.ebi.ac.uk/QuickGO/, so here we will only keep the original BuscoID and the GO ids retrieved with the python script. We will use the package GO.db in the following step to get all GO terms and their ontology classification from each GO id.

buscos_GOs <- read_csv("../data/metazoa_buscos_GOterms.csv") %>%
  filter(grepl("GO:", name)) %>%
  filter(!obsolete_term) %>%
  select(BuscoID = orthodb_id, GOID = id, ancestor_terms,
         count, obsolete_term)
New names:Rows: 8456 Columns: 10── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (7): description, type, id, ontology_type, orthodb_id, name, ancestor_terms
dbl (2): ...1, count
lgl (1): obsolete_term
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
buscos_GOs

The data above only has the specific GO ids/terms for the Busco genes, but not the associated higher level terms. Let’s expand the lists of ancestor_terms, and bind it with the original data set, so that each term (specific and ancestral) has its own line:

metazoanGO <- buscos_GOs %>%
  mutate(ancestor_terms = str_split(ancestor_terms, ",")) %>%
  select(BuscoID, GOID=ancestor_terms) %>%
  unnest(GOID) %>%
  bind_rows(select(buscos_GOs, BuscoID, GOID)) %>%
  rowwise() %>%
  mutate(GO_term = Term(GOID),
         GO_category = Ontology(GOID)) %>%
  ungroup %>%
  filter(!is.na(GO_category)) # GO ids that did not result in any terms
                      # quickgo shows they are secondary ids of other GOs already represented
metazoanGO

Prepare GO universe input files for enrichment analysis: From help of enricher(): TERM2GENE: user input annotation of TERM TO GENE mapping, a data.frame of 2 column with term and gene.

term2gene <- metazoanGO %>%
  split(f = as.factor(.$GO_category)) %>%
  purrr::map(~select(.x, GOID, BuscoID))

term2gene
$BP

$CC

$MF
NA

This one is optional, but it’s how we keep the GO term name: From help of enricher(): TERM2NAME: user input of TERM TO NAME mapping, a data.frame of 2 column with term and name.

term2name <- metazoanGO %>%
  split(f = as.factor(.$GO_category)) %>%
  purrr::map(~select(.x, GOID, GO_term))

term2name
$BP

$CC

$MF
NA

Genes of interest

Input file for set of genes of interest to test for enrichment (should be a set of unique ids): buscos that are missing from both Acutogordius and Nectonema. The code creates a T/F column for whether or not the busco is missing, then groups by BuscoID and sums TRUEs, so that we get a column where 0 = not missing in either, 1 = missing in one of the species, 2 = missing in both species:

overlap_missing <- buscos %>%
  mutate(is_missing = Status == "Missing") %>%
  group_by(BuscoID) %>%
  summarize(Nmissing = sum(is_missing)) %>%
  filter(Nmissing == 2) %>%
  pull(BuscoID)
#overlap_missing

Over-representation analysis

Now put all together in clusterprofiler analysis. For each of the three GO categories (Cellular Component, Molecular Function, Biological Process), run enricher function of clusterprofiler:

enrich_GO <- purrr::map2(.x = term2gene, .y = term2name,
                         ~enricher(gene = overlap_missing, TERM2GENE = .x, TERM2NAME = .y))
# assign ontology to variable (says unknown by default)
enrich_GO[[1]]@ontology <- names(enrich_GO)[1]
enrich_GO[[2]]@ontology <- names(enrich_GO)[2]
enrich_GO[[3]]@ontology <- names(enrich_GO)[3]
enrich_GO#[[1]]@result %>% filter(p.adjust < 0.05)
$BP
#
# over-representation test
#
#...@organism    UNKNOWN 
#...@ontology    BP 
#...@gene    chr [1:220] "107574at33208" "108764at33208" "111730at33208" "111746at33208" "114954at33208" ...
#...pvalues adjusted by 'BH' with cutoff <0.05 
#...37 enriched terms found
'data.frame':   37 obs. of  9 variables:
 $ ID         : chr  "GO:0030030" "GO:0120036" "GO:0044782" "GO:0030031" ...
 $ Description: chr  "cell projection organization" "plasma membrane bounded cell projection organization" "cilium organization" "cell projection assembly" ...
 $ GeneRatio  : chr  "13/139" "13/139" "12/139" "11/139" ...
 $ BgRatio    : chr  "14/673" "14/673" "13/673" "12/673" ...
 $ pvalue     : num  9.03e-09 9.03e-09 4.36e-08 2.08e-07 2.08e-07 ...
 $ p.adjust   : num  1.16e-06 1.16e-06 3.72e-06 8.89e-06 8.89e-06 ...
 $ qvalue     : num  9.08e-07 9.08e-07 2.92e-06 6.98e-06 6.98e-06 ...
 $ geneID     : chr  "111730at33208/119591at33208/157138at33208/218441at33208/257339at33208/284059at33208/315044at33208/334820at33208"| __truncated__ "111730at33208/119591at33208/157138at33208/218441at33208/257339at33208/284059at33208/315044at33208/334820at33208"| __truncated__ "111730at33208/119591at33208/218441at33208/257339at33208/284059at33208/315044at33208/334820at33208/340577at33208"| __truncated__ "111730at33208/119591at33208/218441at33208/257339at33208/284059at33208/315044at33208/334820at33208/340577at33208"| __truncated__ ...
 $ Count      : int  13 13 12 11 11 11 44 15 45 42 ...
#...Citation
 T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu.
 clusterProfiler 4.0: A universal enrichment tool for interpreting omics data.
 The Innovation. 2021, 2(3):100141 


$CC
#
# over-representation test
#
#...@organism    UNKNOWN 
#...@ontology    CC 
#...@gene    chr [1:220] "107574at33208" "108764at33208" "111730at33208" "111746at33208" "114954at33208" ...
#...pvalues adjusted by 'BH' with cutoff <0.05 
#...5 enriched terms found
'data.frame':   5 obs. of  9 variables:
 $ ID         : chr  "GO:0042995" "GO:0120025" "GO:0005929" "GO:0015630" ...
 $ Description: chr  "cell projection" "plasma membrane bounded cell projection" "cilium" "microtubule cytoskeleton" ...
 $ GeneRatio  : chr  "10/141" "10/141" "8/141" "11/141" ...
 $ BgRatio    : chr  "13/651" "13/651" "10/651" "17/651" ...
 $ pvalue     : num  2.71e-05 2.71e-05 1.24e-04 1.29e-04 9.47e-04
 $ p.adjust   : num  0.000922 0.000922 0.002196 0.002196 0.012874
 $ qvalue     : num  0.000857 0.000857 0.00204 0.00204 0.011958
 $ geneID     : chr  "137383at33208/157138at33208/315044at33208/334820at33208/340577at33208/378913at33208/518288at33208/534757at33208"| __truncated__ "137383at33208/157138at33208/315044at33208/334820at33208/340577at33208/378913at33208/518288at33208/534757at33208"| __truncated__ "137383at33208/315044at33208/334820at33208/340577at33208/378913at33208/518288at33208/534757at33208/564234at33208" "114954at33208/137383at33208/247183at33208/397659at33208/421732at33208/450643at33208/534757at33208/561248at33208"| __truncated__ ...
 $ Count      : int  10 10 8 11 11
#...Citation
 T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu.
 clusterProfiler 4.0: A universal enrichment tool for interpreting omics data.
 The Innovation. 2021, 2(3):100141 


$MF
#
# over-representation test
#
#...@organism    UNKNOWN 
#...@ontology    MF 
#...@gene    chr [1:220] "107574at33208" "108764at33208" "111730at33208" "111746at33208" "114954at33208" ...
#...pvalues adjusted by 'BH' with cutoff <0.05 
#...1 enriched terms found
'data.frame':   1 obs. of  9 variables:
 $ ID         : chr "GO:0005515"
 $ Description: chr "protein binding"
 $ GeneRatio  : chr "34/114"
 $ BgRatio    : chr "78/566"
 $ pvalue     : num 2.59e-07
 $ p.adjust   : num 2.05e-05
 $ qvalue     : num 2.05e-05
 $ geneID     : chr "14776at33208/152327at33208/247183at33208/259581at33208/272605at33208/281693at33208/285966at33208/302279at33208/"| __truncated__
 $ Count      : int 34
#...Citation
 T Wu, E Hu, S Xu, M Chen, P Guo, Z Dai, T Feng, L Zhou, W Tang, L Zhan, X Fu, S Liu, X Bo, and G Yu.
 clusterProfiler 4.0: A universal enrichment tool for interpreting omics data.
 The Innovation. 2021, 2(3):100141 

Get result df from enrich_GO and add GO definitions and ontology:

sign_GOs <- function(df){
  df %>%
    filter(p.adjust < 0.05) %>%
    mutate(GO_definition = Definition(ID),
           GO_category = Ontology(ID)) %>%
    separate(GeneRatio, into = c("size_missing_term","size_missing_all"),
             sep = '/', remove = F) %>%
    separate(BgRatio, into = c("size_background_term","size_background_all"),
             sep = '/', remove = F) %>%
    mutate_at(vars("size_missing_term","size_missing_all",
                   "size_background_term","size_background_all"), as.numeric) %>%
    mutate(`Missing over background` = size_missing_term/size_background_term) %>%
    select(`GO ID` = ID, `GO term description` = Description, 
           `GO category` = GO_category, `Associated missing BUSCOs` = Count,
           `Missing ratio` = GeneRatio, `Background ratio` = BgRatio,
           `Missing over background`, pvalue, p.adjust, 
           `BUSCO ID` = geneID, `GO term definition` = GO_definition) %>%
    remove_rownames()}

enriched <- enrich_GO %>%
  purrr::map(~sign_GOs(.x@result))
enriched
$BP

$CC

$MF
NA

GeneRatio = k/n, where n is the set of all annotated MISSING buscos, and k is the number of buscos within n that are annotated to that SPECIFIC GO term. The larger the ratio, the more MISSING genes are related to that particular GO category. BgRatio = M/N, where N is the set of all annotated BUSCOS in the background (universal set), and M is the number of buscos within N that are annotated to that SPECIFIC GO term. The larger the ratio, the more OVERALL buscos are annotated for that particular GO category.

By consequence, M should always be larger than k, because it includes the missing buscos, plus any other buscos not missing that are also associated with that specific GO term. Biologically for the nematomorphs, the most relevant enriched genes are the ones where M is the closest to k, which implies that most of the genes in the background set annotated to that category are missing in nematomorphs. (genes with the smallest pvalue)

Count = k, number of annotated missing buscos.

Save supplementary table with significant terms:

bind_rows(enriched) %>% write_csv(., file = "../results/EnrichedGOterms1.csv")

Expand column with which BUSCOs are annotated to each GO term, so that we can count how many unique BUSCOs were significant:

significant_buscos <- bind_rows(enriched) %>%
  mutate(significant_BUSCOs = str_split(`BUSCO ID`, "/")) %>%
  unnest(significant_BUSCOs) %>%
  group_by(`GO category`, significant_BUSCOs) %>%
  summarise(CountGOterms = n()) %>%
  arrange(CountGOterms, `GO category`)
`summarise()` has grouped output by 'GO category'. You can override using the `.groups` argument.
significant_buscos

All unique significant BUSCOs between BP, CC and MF:

unique_significant_buscos
  [1] "111746at33208" "327301at33208" "405257at33208" "585078at33208" "14776at33208"  "152327at33208"
  [7] "247183at33208" "259581at33208" "272605at33208" "281693at33208" "285966at33208" "302279at33208"
 [13] "304388at33208" "359532at33208" "361216at33208" "382000at33208" "386666at33208" "397659at33208"
 [19] "398202at33208" "441575at33208" "447400at33208" "464204at33208" "500808at33208" "509816at33208"
 [25] "515058at33208" "534757at33208" "544919at33208" "558368at33208" "561248at33208" "563359at33208"
 [31] "567650at33208" "568170at33208" "578734at33208" "595010at33208" "608190at33208" "616149at33208"
 [37] "619415at33208" "642773at33208" "464243at33208" "518635at33208" "638541at33208" "114954at33208"
 [43] "157138at33208" "421732at33208" "450643at33208" "631173at33208" "107574at33208" "108764at33208"
 [49] "131848at33208" "200997at33208" "206858at33208" "344611at33208" "365323at33208" "379076at33208"
 [55] "381303at33208" "443305at33208" "454911at33208" "470197at33208" "505718at33208" "508986at33208"
 [61] "56573at33208"  "571944at33208" "589773at33208" "636041at33208" "315044at33208" "334820at33208"
 [67] "340577at33208" "378913at33208" "518288at33208" "260752at33208" "477998at33208" "488635at33208"
 [73] "503828at33208" "580896at33208" "191938at33208" "137383at33208" "564234at33208" "143997at33208"
 [79] "277935at33208" "424646at33208" "432287at33208" "446031at33208" "515528at33208" "544849at33208"
 [85] "545063at33208" "628231at33208" "368565at33208" "514121at33208" "274064at33208" "361100at33208"
 [91] "524148at33208" "560566at33208" "569294at33208" "581543at33208" "239597at33208" "474302at33208"
 [97] "111730at33208" "119591at33208" "218441at33208" "257339at33208" "284059at33208" "295556at33208"
[103] "429011at33208" "433216at33208" "247905at33208" "353768at33208" "436539at33208" "488559at33208"
[109] "495329at33208" "464286at33208" "430701at33208" "92367at33208" 

Order buscos by numbr of GO terms associated with them:

ordered_buscos <- significant_buscos %>%
  group_by(significant_BUSCOs) %>%
  summarize(N_GO = sum(CountGOterms)) %>%
  arrange(N_GO) %>%
  mutate(significant_BUSCOs = fct_reorder(significant_BUSCOs, N_GO))

Plot number of GO terms for each missing BUSCO:

p_significant_buscos <- significant_buscos %>%
  ggplot() +
  geom_col(aes(x = factor(significant_BUSCOs, levels = ordered_buscos$significant_BUSCOs),
               y = CountGOterms, fill = `GO category`)) +
  scale_fill_brewer(type = "qual") +
  xlab("BUSCO ID") + ylab("Number of annotated GO terms") +
  #ggtitle("Number of GO terms for each Missing BUSCO") +
  theme_minimal() +
  theme(axis.text.x = element_blank())
p_significant_buscos

ggsave("../figures/enrichment/buscos.pdf", p_significant_buscos,
       width = 6.75, height = 4, units = "in", useDingbats = F)

Plot enrichment

https://guangchuangyu.github.io/2016/01/go-analysis-using-clusterprofiler/

Enrichment map: Visualizes gene sets as a network. Mutually overlapping gene sets tend to cluster together, making it easier for interpretation. When the similarity between terms meets a certain threshold (default is 0.2, adjusted by parameter ‘min_edge’), there will be edges between terms. The stronger the similarity, the shorter and thicker the edges.

set.seed(5) #2 without clusters also good
p_map <- enrich_GO %>%
  purrr::map(~emapplot(pairwise_termsim(.x),
                       showCategory=18, shadowtext = F,
                       layout.params = list('gem'),
                       cex.params = list(line=.3, category_label = .6, category_node = .7),
                       #cluster.params = list(cluster = T ,legend = T),
                       repel = T))
p_map[[1]]

p_map[[2]]

Save:

p_map_paths <- stringr::str_c("../figures/enrichment/enrichment_map_", names(p_map), ".pdf")
pwalk(.l = list(p_map_paths, p_map),
      ~ggsave(filename = .x, plot = .y,
              width = 10, height = 10, units = "in", useDingbats = F))

Dotplot:

p_dot <- enrich_GO %>%
  purrr::map(~dotplot(.x))
p_dot
$BP

$CC

$MF

Save:

p_dot_paths <- stringr::str_c("../figures/enrichment/enrichment_dot_", names(p_map), ".pdf")
pwalk(.l = list(p_dot_paths, p_dot),
      ~ggsave(filename = .x, plot = .y,
              width = 7, height = 7, units = "in", useDingbats = F))

Similar idea, but based on calculated column of Missing over background BUSCOs:

p_MissingRatio <- bind_rows(enriched) %>%
  ggplot() +
  geom_col(aes(x = reorder(`GO term description`, `Missing over background`),
               y = `Missing over background`,
               fill = `GO category`)) +
  scale_fill_brewer(type = "qual") +
  scale_y_continuous(breaks = c(0,.25,.5,.75,.9), limits = c(0,1)) +
  ylab("Proportion of missing over background BUSCOs") +
  xlab("GO term") +
  #ggtitle("Missing BUSCOs relative to Metazoa set") +
  coord_flip() +
  theme_minimal()
p_MissingRatio

Save:

ggsave("../figures/enrichment/missing_ratio1.pdf", p_MissingRatio,
       width = 6.85, height = 3, units = "in", useDingbats = F)

Gene-concept network, plots linkage of genes and enriched GO terms:

p_network <- enrich_GO %>%
  purrr::map(~cnetplot(.x, #showCategory = 6, # For 6 top BP terms
                       repel = T, shadowtext = 'all')) #, colorEdge = T))
p_network#[[1]]
$BP

$CC

$MF

Only the top 5 significant terms are displayed for simplicity (default), which really only affects BP that has 37 terms total.

Save:

p_net_paths <- stringr::str_c("../figures/enrichment/enrichment_network2_", names(p_map), ".pdf")
pwalk(.l = list(p_net_paths, p_network),
      ~ggsave(filename = .x, plot = .y,
              width = 10, height = 10, units = "in", useDingbats = F))

Directed acyclic graph (DAG) of enriched GO terms:

p_dag <- enrich_GO %>%
  purrr::map(~plotGOgraph(.x, firstSigNodes = 10)) #10 is default

# pdf("../figures/enrichment/enrichment_dag_BP2.pdf") # Saves only first cluster, not the 3
# plot(p_dag[[1]]$complete.dag)
# dev.off()
# 
# pdf("../figures/enrichment/enrichment_dag_CC.pdf")
# plot(p_dag[[2]]$complete.dag)
# dev.off()
# 
# pdf("../figures/enrichment/enrichment_dag_MF.pdf")
# plot(p_dag[[3]]$complete.dag)
# dev.off()
plot(p_dag[[1]]$complete.dag)

plot(p_dag[[2]]$complete.dag)

plot(p_dag[[3]]$complete.dag)

Only the top 10 significant terms are displayed for simplicity (default), which really only affects BP that has 37 terms total. Each node represents a GO term and branches represent the containment relationships and the degree of colors represent the extent of enrichment, with black and white ellipses representing non-significant enrichment and yellow to red representing the gradient from higher to lower p.adjust values (red most significant). The numbers under the GO terms are the ratio of missing buscos annotated with that term over the number of buscos annotated with that term in the reference database (Missing over background column of enriched_GO df). Top 10 of significantly enriched terms are represented in boxes and the rest in ellipses. BP: Among the 37 significantly enriched GO BP terms, the higher numbers were in cilium and cell projection.

Scratch

To quickly look at a GO term: search for id in https://www.ebi.ac.uk/QuickGO/

LS0tCnRpdGxlOiAiQlVTQ08gYW5kIGVucmljaG1lbnQgYW5hbHlzZXMiCmF1dGhvcjogVGF1YW5hIEouIEN1bmhhCmRhdGU6ICJGZWJydWFyeSAyMDIzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgaGlnaGxpZ2h0OiB0YW5nbyAjZGVmYXVsdCwgdGFuZ28sIHB5Z21lbnRzLCBrYXRlLCBtb25vY2hyb21lLCB6ZW5idXJuLCB0ZXh0bWF0ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKLS0tCgpgYGB7cn0KLmxpYlBhdGhzKG5ldyA9ICJsaWJSLyIpCmxpYnJhcnkoVmVubkRpYWdyYW0pCmxpYnJhcnkodmVubmV1bGVyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmxpYnJhcnkoR08uZGIpCmxpYnJhcnkoZW5yaWNocGxvdCkKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyMgQnVzY28gZGF0YQpMb2FkIGJ1c2NvIHJlc3VsdHM6CmBgYHtyfQphY3V0b2dvcmRpdXNfaGljIDwtIHJlYWRfdHN2KCIuLi9yZXN1bHRzL2J1c2NvNTQ1LU5lbWF0b21vcnBoYS9vdXRfbWV0YXpvYV9vZGIxMC9BY3V0b2dvcmRpdXNfYXVzdHJhbGllbnNpcy0xNTIzOTMtSGlDLmZhL3J1bl9tZXRhem9hX29kYjEwL2Z1bGxfdGFibGUudHN2Iiwgc2tpcCA9IDIpCm5lY3RvbmVtYV9kcmFmdCA8LSByZWFkX3RzdigiLi4vcmVzdWx0cy9idXNjbzU0NS1OZW1hdG9tb3JwaGEvb3V0X21ldGF6b2Ffb2RiMTAvTmVjdG9uZW1hX211bmlkYWUtRE5BMDU4MjctRHJhZnQuZmEvcnVuX21ldGF6b2Ffb2RiMTAvZnVsbF90YWJsZS50c3YiLCBza2lwID0gMikKYGBgCgpKb2luIGFsbCBpbiBvbmUgZGF0YSBmcmFtZToKYGBge3J9CmJ1c2NvcyA8LSBiaW5kX3Jvd3MoIkFjdXRvZ29yZGl1c19IaUMiID0gYWN1dG9nb3JkaXVzX2hpYywKICAgICAgICAgICAgICAgICAgICAiTmVjdG9uZW1hX0RyYWZ0IiA9IG5lY3RvbmVtYV9kcmFmdCwKICAgICAgICAgICAgICAgICAgICAuaWQgPSAiYXNzZW1ibHkiKSAlPiUgcmVuYW1lKCJCdXNjb0lEIiA9ICIjIEJ1c2NvIGlkIikKYnVzY29zCmBgYAoKU2V0IG9mIGFsbCA5NTQgTWV0YXpvYSBidXNjb3M6CmBgYHtyfQptZXRhem9hX2dlbmVzIDwtIGJ1c2NvcyAlPiUgcHVsbChCdXNjb0lEKSAlPiUgdW5pcXVlKCkKI3dyaXRlX2NzdjIoYXMuZGF0YS5mcmFtZShtZXRhem9hX2dlbmVzKSwKIyAgICAgICAgICAgZmlsZSA9ICIuLi9kYXRhL21ldGF6b2FfYnVzY29zLmNzdiIsIGNvbF9uYW1lcyA9IEYpCmBgYAoKIyMgRXhwbG9yYXRvcnkgcGxvdHMKTGVuZ3RoIGhpc3RvZ3JhbToKYGBge3J9CmdncGxvdChidXNjb3MpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IExlbmd0aCwgZmlsbCA9IFN0YXR1cykpICsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBmYWNldF93cmFwKH5hc3NlbWJseSwgbmNvbD0xKQpgYGAKCkxlbmd0aCBib3hwbG90OgpgYGB7cn0KZ2dwbG90KGJ1c2NvcykgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IFN0YXR1cywgeSA9IExlbmd0aCwgZmlsbCA9IGFzc2VtYmx5KSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyBWZW5uIGRpYWdyYW0KTGlzdCBvZiBnZW5lcyBwcmVzZW50IGZvciBlYWNoIHNhbXBsZSwgYW5kIHRoZSB0b3RhbCBtZXRhem9hbiBzZXQ6CmBgYHtyfQpwcmVzZW50IDwtIGJ1c2NvcyAlPiUKICBmaWx0ZXIoU3RhdHVzICE9ICJNaXNzaW5nIikgJT4lCiAgd2l0aCguLCBzcGxpdChCdXNjb0lELCBhc3NlbWJseSkpCgpwcmVzZW50JE1ldGF6b2FfYnVzY29zIDwtIG1ldGF6b2FfZ2VuZXMKCnN1bW1hcnkocHJlc2VudCkKYGBgCgpPbmx5IG9wdGlvbiB0aGF0IGF1dG9tYXRpY2FsbHkgcHV0cyBzbWFsbGVyIHNldHMgaW5zaWRlIGxhcmdlciBzZXQsIGJ1dCBjaXJjbGVzIG5vdCBwcm9wb3J0aW9uYWw6CmBgYHtyfQpteUNvbCA8LSBicmV3ZXIucGFsKDMsICJQYXN0ZWwyIikKdmVubmJ1c2NvcyA8LSB2ZW5uLmRpYWdyYW0oeCA9IHByZXNlbnQsIGRpc2FibGUubG9nZ2luZyA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gJy4uL2ZpZ3VyZXMvdmVubl9idXNjb3MvdmVubl9idXNjb3MucG5nJywKICAgICAgICAgICAgIGNhdGVnb3J5Lm5hbWVzID0gYygiQWN1dG9nb3JkaXVzIiAsICJOZWN0b25lbWEiICwgIk1ldGF6b2EgQlVTQ09TIiksCiAgICAgICAgICAgICBpbWFnZXR5cGU9InBuZyIsIHJlc29sdXRpb24gPSAzMDAsCiAgICAgICAgICAgICBoZWlnaHQgPSA0ODAsIHdpZHRoID0gNDgwLAogICAgICAgICAgICAgIyBDaXJjbGVzCiAgICAgICAgICAgICBsd2QgPSAyLCBsdHkgPSAnYmxhbmsnLCBmaWxsID0gbXlDb2wsCiAgICAgICAgICAgICAjIE51bWJlcnMKICAgICAgICAgICAgIGNleCA9IC41LCBmb250ZmFjZSA9ICJib2xkIiwgZm9udGZhbWlseSA9ICJzYW5zIiwKICAgICAgICAgICAgICMgU2V0IG5hbWVzCiAgICAgICAgICAgICBjYXQuY2V4ID0gMC40LCBjYXQuZm9udGZhY2UgPSAiYm9sZCIsIGNhdC5kZWZhdWx0LnBvcyA9ICJ0ZXh0IiwKICAgICAgICAgICAgIGNhdC5mb250ZmFtaWx5ID0gInNhbnMiKQpgYGAKCk9wdGlvbiB3aXRoIGNvcnJlY3QgcHJvcG9ydGlvbnMsIGJ1dCBub3QgbmVzdGVkOgpUaGlzIGlzIHRoZSBvbmUgdXNlZCBmb3IgdGhlIG1haW4gRmlndXJlIGluIHRoZSBtYW51c2NyaXB0LgpgYGB7cn0KcGRmKGZpbGUgPSAiLi4vZmlndXJlcy92ZW5uX2J1c2Nvcy92ZW5uX3Byb3AucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQogIHBsb3QodmVubmV1bGVyKGMoQWN1dG9nb3JkaXVzPTc4LCBOZWN0b25lbWE9ODgsIE1ldGF6b2FCdXNjb3M9OTU0LAogICAgICAgICAgICAgICAgICJBY3V0b2dvcmRpdXMmTmVjdG9uZW1hIj01NjgsICJBY3V0b2dvcmRpdXMmTWV0YXpvYUJ1c2NvcyI9NzgsCiAgICAgICAgICAgICAgICAgIk5lY3RvbmVtYSZNZXRhem9hQnVzY29zIj04OCAsIkFjdXRvZ29yZGl1cyZOZWN0b25lbWEmTWV0YXpvYUJ1c2NvcyI9NzM0KSkpCmRldi5vZmYoKQpgYGAKCkNoaS1zcXVhcmUgdGVzdCBmb3Igb3ZlcmxhcCBvZiAyMjAgbWlzc2luZyBnZW5lcy4gRmlyc3QgY3JlYXRlIGEgbWF0cml4IHdpdGggbnVtYmVyIG9mIGdlbmVzIGZvciBlYWNoL2JvdGggc3BlY2llcywgdGFrZW4gZWFzaWx5IGZyb20gdGhlIHZlbm5fYnVzY29zLnBuZyBmaWxlIGNyZWF0ZWQganVzdCBhYm92ZS4KYGBge3J9CiMgY29scyA9IHByZXNlbnQgaW4gQWN1dG8sIGFic2VudCBpbiBBY3V0bwojIHJvd3MgPSBwcmVzZW50IGluIE5lY3RvLCBhYnNlbnQgaW4gTmVjdG8KY2hpbWF0cml4IDwtIG1hdHJpeChjKDU2OCwgODgsIDc4LCAyMjApLCBuY29sID0gMikKY2hpbWF0cml4CgpjaGlzcS50ZXN0KGNoaW1hdHJpeCkKYGBgCgoKIyMgRW5yaWNobWVudCBhbmFseXNpcwoKSHlwZXJnZW9tZXRyaWMgZW5yaWNobWVudCAoPSBzaW1wbGUgZW5yaWNobWVudCBvciBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzKSB0byBhc3Nlc3Mgd2hldGhlciB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgYnVzY29zIGFzc29jaWF0ZWQgd2l0aCBhIGdpdmVuIGZ1bmN0aW9uIGlzIGxhcmdlciB0aGFuIGV4cGVjdGVkIGJ5IGNoYW5jZS4KCjEtIERlZmluZSBHTyB1bml2ZXJzZSwgdGhlIGJhY2tncm91bmQgc2V0IG9mIGFsbCBHTyB0ZXJtcyBhZ2FpbnN0IHdoaWNoIHdlIHdpbGwgdGVzdCBmb3IgZW5yaWNobWVudC4gSXQgc2hvdWxkIGluY2x1ZGUgYW55IGdlbmUgdGhhdCBjb3VsZCBoYXZlIGJlZW4gcG9zaXRpdmUgKD5HTyB0ZXJtcyBvZiBhbGwgOTU0IG1ldGF6b2FuIGJ1c2NvcyBhbmQgdGhlaXIgR08gYW5jZXN0cmFsIHRlcm1zKQoyLSBEZWZpbmUgZ2VuZXMgc2lnbmlmaWNhbnQgZm9yIHZhcmlhYmxlIG9mIGludGVyZXN0ICg+bWlzc2luZyBidXNjb3MgaW4gZ2Vub21lIG9mIG5lbWF0b21vcnBocykKMy0gQ2FsY3VsYXRlIHByb3BvcnRpb24gb2Ygc2lnbmlmaWNhbnQgZ2VuZXMgaW4gZ2VuZSBzZXQKNC0gRXN0aW1hdGUgcHJvYmFiaWxpdHkgYW5kIHNpZ25pZmljYW5jZSAocC12YWx1ZSkKCiMjIyBHTyB1bml2ZXJzZQpTdGFydCB3aXRoIGxvYWRpbmcgdGhlIGRhdGEgd2UgZ290IHdpdGggYSBjdXN0b20gcHl0aG9uIHNjcmlwdCAoYnVzY29zX3RvX0dPLnB5KSB0aGF0IHJldHJpZXZlcyBHTyBpZHMvdGVybXMgZnJvbSB0aGUgT3J0aG9EQiB2MTAgYmFzZWQgb24gdGhlIGxpc3Qgb2YgYWxsIDk1NCBNZXRhem9hbiBCdXNjbyBJRHMuIFRoZW4gZmlsdGVyIG9ubHkgZm9yIEdPIHRlcm1zICh0aGVyZSBhcmUgaW50ZXJwcm8gYW5kIG90aGVyIGxpbmVzIHRoZXJlIHRvbykuClRoZSB0ZXJtcyB0aGVtc2VsdmVzIHdlcmUgbm90IGNvbXBsZXRlIChOQSBmb3IgdW5jbGFzc2lmaWVkIEdPcyksIGFuZCBldmVuIHRob3NlIGRvIGhhdmUgY2xhc3NpZmljYXRpb24gaWYgeW91IGxvb2sgdGhlbSB1cCBpbiBodHRwczovL3d3dy5lYmkuYWMudWsvUXVpY2tHTy8sIHNvIGhlcmUgd2Ugd2lsbCBvbmx5IGtlZXAgdGhlIG9yaWdpbmFsIEJ1c2NvSUQgYW5kIHRoZSBHTyBpZHMgcmV0cmlldmVkIHdpdGggdGhlIHB5dGhvbiBzY3JpcHQuIFdlIHdpbGwgdXNlIHRoZSBwYWNrYWdlIEdPLmRiIGluIHRoZSBmb2xsb3dpbmcgc3RlcCB0byBnZXQgYWxsIEdPIHRlcm1zIGFuZCB0aGVpciBvbnRvbG9neSBjbGFzc2lmaWNhdGlvbiBmcm9tIGVhY2ggR08gaWQuCmBgYHtyfQpidXNjb3NfR09zIDwtIHJlYWRfY3N2KCIuLi9kYXRhL21ldGF6b2FfYnVzY29zX0dPdGVybXMuY3N2IikgJT4lCiAgZmlsdGVyKGdyZXBsKCJHTzoiLCBuYW1lKSkgJT4lCiAgZmlsdGVyKCFvYnNvbGV0ZV90ZXJtKSAlPiUKICBzZWxlY3QoQnVzY29JRCA9IG9ydGhvZGJfaWQsIEdPSUQgPSBpZCwgYW5jZXN0b3JfdGVybXMsCiAgICAgICAgIGNvdW50LCBvYnNvbGV0ZV90ZXJtKQpidXNjb3NfR09zCmBgYAoKVGhlIGRhdGEgYWJvdmUgb25seSBoYXMgdGhlIHNwZWNpZmljIEdPIGlkcy90ZXJtcyBmb3IgdGhlIEJ1c2NvIGdlbmVzLCBidXQgbm90IHRoZSBhc3NvY2lhdGVkIGhpZ2hlciBsZXZlbCB0ZXJtcy4gTGV0J3MgZXhwYW5kIHRoZSBsaXN0cyBvZiBhbmNlc3Rvcl90ZXJtcywgYW5kIGJpbmQgaXQgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YSBzZXQsIHNvIHRoYXQgZWFjaCB0ZXJtIChzcGVjaWZpYyBhbmQgYW5jZXN0cmFsKSBoYXMgaXRzIG93biBsaW5lOgpgYGB7cn0KbWV0YXpvYW5HTyA8LSBidXNjb3NfR09zICU+JQogIG11dGF0ZShhbmNlc3Rvcl90ZXJtcyA9IHN0cl9zcGxpdChhbmNlc3Rvcl90ZXJtcywgIiwiKSkgJT4lCiAgc2VsZWN0KEJ1c2NvSUQsIEdPSUQ9YW5jZXN0b3JfdGVybXMpICU+JQogIHVubmVzdChHT0lEKSAlPiUKICBiaW5kX3Jvd3Moc2VsZWN0KGJ1c2Nvc19HT3MsIEJ1c2NvSUQsIEdPSUQpKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKEdPX3Rlcm0gPSBUZXJtKEdPSUQpLAogICAgICAgICBHT19jYXRlZ29yeSA9IE9udG9sb2d5KEdPSUQpKSAlPiUKICB1bmdyb3VwICU+JQogIGZpbHRlcighaXMubmEoR09fY2F0ZWdvcnkpKSAjIEdPIGlkcyB0aGF0IGRpZCBub3QgcmVzdWx0IGluIGFueSB0ZXJtcwogICAgICAgICAgICAgICAgICAgICAgIyBxdWlja2dvIHNob3dzIHRoZXkgYXJlIHNlY29uZGFyeSBpZHMgb2Ygb3RoZXIgR09zIGFscmVhZHkgcmVwcmVzZW50ZWQKbWV0YXpvYW5HTwpgYGAKClByZXBhcmUgR08gdW5pdmVyc2UgaW5wdXQgZmlsZXMgZm9yIGVucmljaG1lbnQgYW5hbHlzaXM6CkZyb20gaGVscCBvZiBlbnJpY2hlcigpOiBURVJNMkdFTkU6IHVzZXIgaW5wdXQgYW5ub3RhdGlvbiBvZiBURVJNIFRPIEdFTkUgbWFwcGluZywgYSBkYXRhLmZyYW1lIG9mIDIgY29sdW1uIHdpdGggdGVybSBhbmQgZ2VuZS4KYGBge3J9CnRlcm0yZ2VuZSA8LSBtZXRhem9hbkdPICU+JQogIHNwbGl0KGYgPSBhcy5mYWN0b3IoLiRHT19jYXRlZ29yeSkpICU+JQogIHB1cnJyOjptYXAofnNlbGVjdCgueCwgR09JRCwgQnVzY29JRCkpCgp0ZXJtMmdlbmUKYGBgCgpUaGlzIG9uZSBpcyBvcHRpb25hbCwgYnV0IGl0J3MgaG93IHdlIGtlZXAgdGhlIEdPIHRlcm0gbmFtZToKRnJvbSBoZWxwIG9mIGVucmljaGVyKCk6IFRFUk0yTkFNRTogdXNlciBpbnB1dCBvZiBURVJNIFRPIE5BTUUgbWFwcGluZywgYSBkYXRhLmZyYW1lIG9mIDIgY29sdW1uIHdpdGggdGVybSBhbmQgbmFtZS4KYGBge3J9CnRlcm0ybmFtZSA8LSBtZXRhem9hbkdPICU+JQogIHNwbGl0KGYgPSBhcy5mYWN0b3IoLiRHT19jYXRlZ29yeSkpICU+JQogIHB1cnJyOjptYXAofnNlbGVjdCgueCwgR09JRCwgR09fdGVybSkpCgp0ZXJtMm5hbWUKYGBgCgojIyMgR2VuZXMgb2YgaW50ZXJlc3QKSW5wdXQgZmlsZSBmb3Igc2V0IG9mIGdlbmVzIG9mIGludGVyZXN0IHRvIHRlc3QgZm9yIGVucmljaG1lbnQgKHNob3VsZCBiZSBhIHNldCBvZiB1bmlxdWUgaWRzKTogYnVzY29zIHRoYXQgYXJlIG1pc3NpbmcgZnJvbSBib3RoIEFjdXRvZ29yZGl1cyBhbmQgTmVjdG9uZW1hLiBUaGUgY29kZSBjcmVhdGVzIGEgVC9GIGNvbHVtbiBmb3Igd2hldGhlciBvciBub3QgdGhlIGJ1c2NvIGlzIG1pc3NpbmcsIHRoZW4gZ3JvdXBzIGJ5IEJ1c2NvSUQgYW5kIHN1bXMgVFJVRXMsIHNvIHRoYXQgd2UgZ2V0IGEgY29sdW1uIHdoZXJlIDAgPSBub3QgbWlzc2luZyBpbiBlaXRoZXIsIDEgPSBtaXNzaW5nIGluIG9uZSBvZiB0aGUgc3BlY2llcywgMiA9IG1pc3NpbmcgaW4gYm90aCBzcGVjaWVzOgpgYGB7cn0Kb3ZlcmxhcF9taXNzaW5nIDwtIGJ1c2NvcyAlPiUKICBtdXRhdGUoaXNfbWlzc2luZyA9IFN0YXR1cyA9PSAiTWlzc2luZyIpICU+JQogIGdyb3VwX2J5KEJ1c2NvSUQpICU+JQogIHN1bW1hcml6ZShObWlzc2luZyA9IHN1bShpc19taXNzaW5nKSkgJT4lCiAgZmlsdGVyKE5taXNzaW5nID09IDIpICU+JQogIHB1bGwoQnVzY29JRCkKI292ZXJsYXBfbWlzc2luZwpgYGAKCiMjIyBPdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzCk5vdyBwdXQgYWxsIHRvZ2V0aGVyIGluIGNsdXN0ZXJwcm9maWxlciBhbmFseXNpcy4gRm9yIGVhY2ggb2YgdGhlIHRocmVlIEdPIGNhdGVnb3JpZXMgKENlbGx1bGFyIENvbXBvbmVudCwgTW9sZWN1bGFyIEZ1bmN0aW9uLCBCaW9sb2dpY2FsIFByb2Nlc3MpLCBydW4gZW5yaWNoZXIgZnVuY3Rpb24gb2YgY2x1c3RlcnByb2ZpbGVyOgpgYGB7cn0KZW5yaWNoX0dPIDwtIHB1cnJyOjptYXAyKC54ID0gdGVybTJnZW5lLCAueSA9IHRlcm0ybmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgIH5lbnJpY2hlcihnZW5lID0gb3ZlcmxhcF9taXNzaW5nLCBURVJNMkdFTkUgPSAueCwgVEVSTTJOQU1FID0gLnkpKQojIGFzc2lnbiBvbnRvbG9neSB0byB2YXJpYWJsZSAoc2F5cyB1bmtub3duIGJ5IGRlZmF1bHQpCmVucmljaF9HT1tbMV1dQG9udG9sb2d5IDwtIG5hbWVzKGVucmljaF9HTylbMV0KZW5yaWNoX0dPW1syXV1Ab250b2xvZ3kgPC0gbmFtZXMoZW5yaWNoX0dPKVsyXQplbnJpY2hfR09bWzNdXUBvbnRvbG9neSA8LSBuYW1lcyhlbnJpY2hfR08pWzNdCmVucmljaF9HTyNbWzFdXUByZXN1bHQgJT4lIGZpbHRlcihwLmFkanVzdCA8IDAuMDUpCmBgYAoKR2V0IHJlc3VsdCBkZiBmcm9tIGVucmljaF9HTyBhbmQgYWRkIEdPIGRlZmluaXRpb25zIGFuZCBvbnRvbG9neToKYGBge3J9CnNpZ25fR09zIDwtIGZ1bmN0aW9uKGRmKXsKICBkZiAlPiUKICAgIGZpbHRlcihwLmFkanVzdCA8IDAuMDUpICU+JQogICAgbXV0YXRlKEdPX2RlZmluaXRpb24gPSBEZWZpbml0aW9uKElEKSwKICAgICAgICAgICBHT19jYXRlZ29yeSA9IE9udG9sb2d5KElEKSkgJT4lCiAgICBzZXBhcmF0ZShHZW5lUmF0aW8sIGludG8gPSBjKCJzaXplX21pc3NpbmdfdGVybSIsInNpemVfbWlzc2luZ19hbGwiKSwKICAgICAgICAgICAgIHNlcCA9ICcvJywgcmVtb3ZlID0gRikgJT4lCiAgICBzZXBhcmF0ZShCZ1JhdGlvLCBpbnRvID0gYygic2l6ZV9iYWNrZ3JvdW5kX3Rlcm0iLCJzaXplX2JhY2tncm91bmRfYWxsIiksCiAgICAgICAgICAgICBzZXAgPSAnLycsIHJlbW92ZSA9IEYpICU+JQogICAgbXV0YXRlX2F0KHZhcnMoInNpemVfbWlzc2luZ190ZXJtIiwic2l6ZV9taXNzaW5nX2FsbCIsCiAgICAgICAgICAgICAgICAgICAic2l6ZV9iYWNrZ3JvdW5kX3Rlcm0iLCJzaXplX2JhY2tncm91bmRfYWxsIiksIGFzLm51bWVyaWMpICU+JQogICAgbXV0YXRlKGBNaXNzaW5nIG92ZXIgYmFja2dyb3VuZGAgPSBzaXplX21pc3NpbmdfdGVybS9zaXplX2JhY2tncm91bmRfdGVybSkgJT4lCiAgICBzZWxlY3QoYEdPIElEYCA9IElELCBgR08gdGVybSBkZXNjcmlwdGlvbmAgPSBEZXNjcmlwdGlvbiwgCiAgICAgICAgICAgYEdPIGNhdGVnb3J5YCA9IEdPX2NhdGVnb3J5LCBgQXNzb2NpYXRlZCBtaXNzaW5nIEJVU0NPc2AgPSBDb3VudCwKICAgICAgICAgICBgTWlzc2luZyByYXRpb2AgPSBHZW5lUmF0aW8sIGBCYWNrZ3JvdW5kIHJhdGlvYCA9IEJnUmF0aW8sCiAgICAgICAgICAgYE1pc3Npbmcgb3ZlciBiYWNrZ3JvdW5kYCwgcHZhbHVlLCBwLmFkanVzdCwgCiAgICAgICAgICAgYEJVU0NPIElEYCA9IGdlbmVJRCwgYEdPIHRlcm0gZGVmaW5pdGlvbmAgPSBHT19kZWZpbml0aW9uKSAlPiUKICAgIHJlbW92ZV9yb3duYW1lcygpfQoKZW5yaWNoZWQgPC0gZW5yaWNoX0dPICU+JQogIHB1cnJyOjptYXAofnNpZ25fR09zKC54QHJlc3VsdCkpCmVucmljaGVkCmBgYAoKR2VuZVJhdGlvID0gay9uLCB3aGVyZSBuIGlzIHRoZSBzZXQgb2YgKmFsbCBhbm5vdGF0ZWQgTUlTU0lORyBidXNjb3MqLCBhbmQgayBpcyB0aGUgbnVtYmVyIG9mIGJ1c2NvcyB3aXRoaW4gbiB0aGF0IGFyZSAqYW5ub3RhdGVkIHRvIHRoYXQgU1BFQ0lGSUMgR08gdGVybSouIFRoZSBsYXJnZXIgdGhlIHJhdGlvLCB0aGUgbW9yZSBNSVNTSU5HIGdlbmVzIGFyZSByZWxhdGVkIHRvIHRoYXQgcGFydGljdWxhciBHTyBjYXRlZ29yeS4KQmdSYXRpbyA9IE0vTiwgd2hlcmUgTiBpcyB0aGUgc2V0IG9mICphbGwgYW5ub3RhdGVkIEJVU0NPUyBpbiB0aGUgYmFja2dyb3VuZCogKHVuaXZlcnNhbCBzZXQpLCBhbmQgTSBpcyB0aGUgbnVtYmVyIG9mIGJ1c2NvcyB3aXRoaW4gTiB0aGF0IGFyZSAqYW5ub3RhdGVkIHRvIHRoYXQgU1BFQ0lGSUMgR08gdGVybSouIFRoZSBsYXJnZXIgdGhlIHJhdGlvLCB0aGUgbW9yZSBPVkVSQUxMIGJ1c2NvcyBhcmUgYW5ub3RhdGVkIGZvciB0aGF0IHBhcnRpY3VsYXIgR08gY2F0ZWdvcnkuCgpCeSBjb25zZXF1ZW5jZSwgTSBzaG91bGQgYWx3YXlzIGJlIGxhcmdlciB0aGFuIGssIGJlY2F1c2UgaXQgaW5jbHVkZXMgdGhlIG1pc3NpbmcgYnVzY29zLCBwbHVzIGFueSBvdGhlciBidXNjb3Mgbm90IG1pc3NpbmcgdGhhdCBhcmUgYWxzbyBhc3NvY2lhdGVkIHdpdGggdGhhdCBzcGVjaWZpYyBHTyB0ZXJtLiAqQmlvbG9naWNhbGx5IGZvciB0aGUgbmVtYXRvbW9ycGhzLCB0aGUgbW9zdCByZWxldmFudCBlbnJpY2hlZCBnZW5lcyBhcmUgdGhlIG9uZXMgd2hlcmUgTSBpcyB0aGUgY2xvc2VzdCB0byBrLCB3aGljaCBpbXBsaWVzIHRoYXQgbW9zdCBvZiB0aGUgZ2VuZXMgaW4gdGhlIGJhY2tncm91bmQgc2V0IGFubm90YXRlZCB0byB0aGF0IGNhdGVnb3J5IGFyZSBtaXNzaW5nIGluIG5lbWF0b21vcnBocy4qIChnZW5lcyB3aXRoIHRoZSBzbWFsbGVzdCBwdmFsdWUpCgpDb3VudCA9IGssIG51bWJlciBvZiBhbm5vdGF0ZWQgbWlzc2luZyBidXNjb3MuCgpTYXZlIHN1cHBsZW1lbnRhcnkgdGFibGUgd2l0aCBzaWduaWZpY2FudCB0ZXJtczoKYGBge3J9CmJpbmRfcm93cyhlbnJpY2hlZCkgJT4lIHdyaXRlX2NzdiguLCBmaWxlID0gIi4uL3Jlc3VsdHMvRW5yaWNoZWRHT3Rlcm1zLmNzdiIpCmBgYAoKRXhwYW5kIGNvbHVtbiB3aXRoIHdoaWNoIEJVU0NPcyBhcmUgYW5ub3RhdGVkIHRvIGVhY2ggR08gdGVybSwgc28gdGhhdCB3ZSBjYW4gY291bnQgaG93IG1hbnkgdW5pcXVlIEJVU0NPcyB3ZXJlIHNpZ25pZmljYW50OgpgYGB7cn0Kc2lnbmlmaWNhbnRfYnVzY29zIDwtIGJpbmRfcm93cyhlbnJpY2hlZCkgJT4lCiAgbXV0YXRlKHNpZ25pZmljYW50X0JVU0NPcyA9IHN0cl9zcGxpdChgQlVTQ08gSURgLCAiLyIpKSAlPiUKICB1bm5lc3Qoc2lnbmlmaWNhbnRfQlVTQ09zKSAlPiUKICBncm91cF9ieShgR08gY2F0ZWdvcnlgLCBzaWduaWZpY2FudF9CVVNDT3MpICU+JQogIHN1bW1hcmlzZShDb3VudEdPdGVybXMgPSBuKCkpICU+JQogIGFycmFuZ2UoQ291bnRHT3Rlcm1zLCBgR08gY2F0ZWdvcnlgKQpzaWduaWZpY2FudF9idXNjb3MKYGBgCgpBbGwgdW5pcXVlIHNpZ25pZmljYW50IEJVU0NPcyBiZXR3ZWVuIEJQLCBDQyBhbmQgTUY6CmBgYHtyfQp1bmlxdWVfc2lnbmlmaWNhbnRfYnVzY29zIDwtIHNpZ25pZmljYW50X2J1c2NvcyAlPiUgcHVsbChzaWduaWZpY2FudF9CVVNDT3MpICU+JSB1bmlxdWUKdW5pcXVlX3NpZ25pZmljYW50X2J1c2NvcwojdW5pcXVlX3NpZ25pZmljYW50X2J1c2NvcyAlaW4lIG92ZXJsYXBfbWlzc2luZwpgYGAKCk9yZGVyIGJ1c2NvcyBieSBudW1iciBvZiBHTyB0ZXJtcyBhc3NvY2lhdGVkIHdpdGggdGhlbToKYGBge3J9Cm9yZGVyZWRfYnVzY29zIDwtIHNpZ25pZmljYW50X2J1c2NvcyAlPiUKICBncm91cF9ieShzaWduaWZpY2FudF9CVVNDT3MpICU+JQogIHN1bW1hcml6ZShOX0dPID0gc3VtKENvdW50R090ZXJtcykpICU+JQogIGFycmFuZ2UoTl9HTykgJT4lCiAgbXV0YXRlKHNpZ25pZmljYW50X0JVU0NPcyA9IGZjdF9yZW9yZGVyKHNpZ25pZmljYW50X0JVU0NPcywgTl9HTykpCmBgYAoKUGxvdCBudW1iZXIgb2YgR08gdGVybXMgZm9yIGVhY2ggbWlzc2luZyBCVVNDTzoKYGBge3J9CnBfc2lnbmlmaWNhbnRfYnVzY29zIDwtIHNpZ25pZmljYW50X2J1c2NvcyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9jb2woYWVzKHggPSBmYWN0b3Ioc2lnbmlmaWNhbnRfQlVTQ09zLCBsZXZlbHMgPSBvcmRlcmVkX2J1c2NvcyRzaWduaWZpY2FudF9CVVNDT3MpLAogICAgICAgICAgICAgICB5ID0gQ291bnRHT3Rlcm1zLCBmaWxsID0gYEdPIGNhdGVnb3J5YCkpICsKICBzY2FsZV9maWxsX2JyZXdlcih0eXBlID0gInF1YWwiKSArCiAgeGxhYigiQlVTQ08gSUQiKSArIHlsYWIoIk51bWJlciBvZiBhbm5vdGF0ZWQgR08gdGVybXMiKSArCiAgI2dndGl0bGUoIk51bWJlciBvZiBHTyB0ZXJtcyBmb3IgZWFjaCBNaXNzaW5nIEJVU0NPIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpCnBfc2lnbmlmaWNhbnRfYnVzY29zCmBgYAoKYGBge3J9Cmdnc2F2ZSgiLi4vZmlndXJlcy9lbnJpY2htZW50L2J1c2Nvcy5wZGYiLCBwX3NpZ25pZmljYW50X2J1c2NvcywKICAgICAgIHdpZHRoID0gNi43NSwgaGVpZ2h0ID0gNCwgdW5pdHMgPSAiaW4iLCB1c2VEaW5nYmF0cyA9IEYpCmBgYAoKCiMjIyBQbG90IGVucmljaG1lbnQKaHR0cHM6Ly9ndWFuZ2NodWFuZ3l1LmdpdGh1Yi5pby8yMDE2LzAxL2dvLWFuYWx5c2lzLXVzaW5nLWNsdXN0ZXJwcm9maWxlci8KCkVucmljaG1lbnQgbWFwOgpWaXN1YWxpemVzIGdlbmUgc2V0cyBhcyBhIG5ldHdvcmsuIE11dHVhbGx5IG92ZXJsYXBwaW5nIGdlbmUgc2V0cyB0ZW5kIHRvIGNsdXN0ZXIgdG9nZXRoZXIsIG1ha2luZyBpdCBlYXNpZXIgZm9yIGludGVycHJldGF0aW9uLiBXaGVuIHRoZSBzaW1pbGFyaXR5IGJldHdlZW4gdGVybXMgbWVldHMgYSBjZXJ0YWluIHRocmVzaG9sZCAoZGVmYXVsdCBpcyAwLjIsIGFkanVzdGVkIGJ5IHBhcmFtZXRlciAnbWluX2VkZ2UnKSwgdGhlcmUgd2lsbCBiZSBlZGdlcyBiZXR3ZWVuIHRlcm1zLiBUaGUgc3Ryb25nZXIgdGhlIHNpbWlsYXJpdHksIHRoZSBzaG9ydGVyIGFuZCB0aGlja2VyIHRoZSBlZGdlcy4KYGBge3J9CnNldC5zZWVkKDUpICMyIHdpdGhvdXQgY2x1c3RlcnMgYWxzbyBnb29kCnBfbWFwIDwtIGVucmljaF9HTyAlPiUKICBwdXJycjo6bWFwKH5lbWFwcGxvdChwYWlyd2lzZV90ZXJtc2ltKC54KSwKICAgICAgICAgICAgICAgICAgICAgICBzaG93Q2F0ZWdvcnk9MTgsIHNoYWRvd3RleHQgPSBGLAogICAgICAgICAgICAgICAgICAgICAgIGxheW91dC5wYXJhbXMgPSBsaXN0KCdnZW0nKSwKICAgICAgICAgICAgICAgICAgICAgICBjZXgucGFyYW1zID0gbGlzdChsaW5lPS4zLCBjYXRlZ29yeV9sYWJlbCA9IC42LCBjYXRlZ29yeV9ub2RlID0gLjcpLAogICAgICAgICAgICAgICAgICAgICAgICNjbHVzdGVyLnBhcmFtcyA9IGxpc3QoY2x1c3RlciA9IFQgLGxlZ2VuZCA9IFQpLAogICAgICAgICAgICAgICAgICAgICAgIHJlcGVsID0gVCkpCnBfbWFwW1sxXV0KcF9tYXBbWzJdXQpgYGAKClNhdmU6CmBgYHtyfQpwX21hcF9wYXRocyA8LSBzdHJpbmdyOjpzdHJfYygiLi4vZmlndXJlcy9lbnJpY2htZW50L2VucmljaG1lbnRfbWFwXyIsIG5hbWVzKHBfbWFwKSwgIi5wZGYiKQpwd2FsaygubCA9IGxpc3QocF9tYXBfcGF0aHMsIHBfbWFwKSwKICAgICAgfmdnc2F2ZShmaWxlbmFtZSA9IC54LCBwbG90ID0gLnksCiAgICAgICAgICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTAsIHVuaXRzID0gImluIiwgdXNlRGluZ2JhdHMgPSBGKSkKYGBgCgoKRG90cGxvdDoKYGBge3J9CnBfZG90IDwtIGVucmljaF9HTyAlPiUKICBwdXJycjo6bWFwKH5kb3RwbG90KC54KSkKcF9kb3QKYGBgCgpTYXZlOgpgYGB7cn0KcF9kb3RfcGF0aHMgPC0gc3RyaW5ncjo6c3RyX2MoIi4uL2ZpZ3VyZXMvZW5yaWNobWVudC9lbnJpY2htZW50X2RvdF8iLCBuYW1lcyhwX21hcCksICIucGRmIikKcHdhbGsoLmwgPSBsaXN0KHBfZG90X3BhdGhzLCBwX2RvdCksCiAgICAgIH5nZ3NhdmUoZmlsZW5hbWUgPSAueCwgcGxvdCA9IC55LAogICAgICAgICAgICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNywgdW5pdHMgPSAiaW4iLCB1c2VEaW5nYmF0cyA9IEYpKQpgYGAKCgpTaW1pbGFyIGlkZWEsIGJ1dCBiYXNlZCBvbiBjYWxjdWxhdGVkIGNvbHVtbiBvZiBNaXNzaW5nIG92ZXIgYmFja2dyb3VuZCBCVVNDT3M6CmBgYHtyfQpwX01pc3NpbmdSYXRpbyA8LSBiaW5kX3Jvd3MoZW5yaWNoZWQpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2NvbChhZXMoeCA9IHJlb3JkZXIoYEdPIHRlcm0gZGVzY3JpcHRpb25gLCBgTWlzc2luZyBvdmVyIGJhY2tncm91bmRgKSwKICAgICAgICAgICAgICAgeSA9IGBNaXNzaW5nIG92ZXIgYmFja2dyb3VuZGAsCiAgICAgICAgICAgICAgIGZpbGwgPSBgR08gY2F0ZWdvcnlgKSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGUgPSAicXVhbCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYygwLC4yNSwuNSwuNzUsLjkpLCBsaW1pdHMgPSBjKDAsMSkpICsKICB5bGFiKCJQcm9wb3J0aW9uIG9mIG1pc3Npbmcgb3ZlciBiYWNrZ3JvdW5kIEJVU0NPcyIpICsKICB4bGFiKCJHTyB0ZXJtIikgKwogICNnZ3RpdGxlKCJNaXNzaW5nIEJVU0NPcyByZWxhdGl2ZSB0byBNZXRhem9hIHNldCIpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX21pbmltYWwoKQpwX01pc3NpbmdSYXRpbwpgYGAKClNhdmU6CmBgYHtyfQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvZW5yaWNobWVudC9taXNzaW5nX3JhdGlvLnBkZiIsIHBfTWlzc2luZ1JhdGlvLAogICAgICAgd2lkdGggPSA2Ljc1LCBoZWlnaHQgPSA0LCB1bml0cyA9ICJpbiIsIHVzZURpbmdiYXRzID0gRikKYGBgCgoKR2VuZS1jb25jZXB0IG5ldHdvcmssIHBsb3RzIGxpbmthZ2Ugb2YgZ2VuZXMgYW5kIGVucmljaGVkIEdPIHRlcm1zOgpgYGB7cn0KcF9uZXR3b3JrIDwtIGVucmljaF9HTyAlPiUKICBwdXJycjo6bWFwKH5jbmV0cGxvdCgueCwgI3Nob3dDYXRlZ29yeSA9IDYsICMgRm9yIDYgdG9wIEJQIHRlcm1zCiAgICAgICAgICAgICAgICAgICAgICAgcmVwZWwgPSBULCBzaGFkb3d0ZXh0ID0gJ2FsbCcpKSAjLCBjb2xvckVkZ2UgPSBUKSkKcF9uZXR3b3JrI1tbMV1dCmBgYAoKT25seSB0aGUgdG9wIDUgc2lnbmlmaWNhbnQgdGVybXMgYXJlIGRpc3BsYXllZCBmb3Igc2ltcGxpY2l0eSAoZGVmYXVsdCksIHdoaWNoIHJlYWxseSBvbmx5IGFmZmVjdHMgQlAgdGhhdCBoYXMgMzcgdGVybXMgdG90YWwuCgpTYXZlOgpgYGB7cn0KcF9uZXRfcGF0aHMgPC0gc3RyaW5ncjo6c3RyX2MoIi4uL2ZpZ3VyZXMvZW5yaWNobWVudC9lbnJpY2htZW50X25ldHdvcmtfIiwgbmFtZXMocF9tYXApLCAiLnBkZiIpCnB3YWxrKC5sID0gbGlzdChwX25ldF9wYXRocywgcF9uZXR3b3JrKSwKICAgICAgfmdnc2F2ZShmaWxlbmFtZSA9IC54LCBwbG90ID0gLnksCiAgICAgICAgICAgICAgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTAsIHVuaXRzID0gImluIiwgdXNlRGluZ2JhdHMgPSBGKSkKYGBgCgoKRGlyZWN0ZWQgYWN5Y2xpYyBncmFwaCAoREFHKSBvZiBlbnJpY2hlZCBHTyB0ZXJtczoKYGBge3J9CnBfZGFnIDwtIGVucmljaF9HTyAlPiUKICBwdXJycjo6bWFwKH5wbG90R09ncmFwaCgueCwgZmlyc3RTaWdOb2RlcyA9IDEwKSkgIzEwIGlzIGRlZmF1bHQKCiMgcGRmKCIuLi9maWd1cmVzL2VucmljaG1lbnQvZW5yaWNobWVudF9kYWdfQlAyLnBkZiIpICMgU2F2ZXMgb25seSBmaXJzdCBjbHVzdGVyLCBub3QgdGhlIDMKIyBwbG90KHBfZGFnW1sxXV0kY29tcGxldGUuZGFnKQojIGRldi5vZmYoKQojIAojIHBkZigiLi4vZmlndXJlcy9lbnJpY2htZW50L2VucmljaG1lbnRfZGFnX0NDLnBkZiIpCiMgcGxvdChwX2RhZ1tbMl1dJGNvbXBsZXRlLmRhZykKIyBkZXYub2ZmKCkKIyAKIyBwZGYoIi4uL2ZpZ3VyZXMvZW5yaWNobWVudC9lbnJpY2htZW50X2RhZ19NRi5wZGYiKQojIHBsb3QocF9kYWdbWzNdXSRjb21wbGV0ZS5kYWcpCiMgZGV2Lm9mZigpCmBgYAoKCmBgYHtyfQpwbG90KHBfZGFnW1sxXV0kY29tcGxldGUuZGFnKQpgYGAKCgpgYGB7cn0KcGxvdChwX2RhZ1tbMl1dJGNvbXBsZXRlLmRhZykKYGBgCgoKYGBge3J9CnBsb3QocF9kYWdbWzNdXSRjb21wbGV0ZS5kYWcpCmBgYAoKT25seSB0aGUgdG9wIDEwIHNpZ25pZmljYW50IHRlcm1zIGFyZSBkaXNwbGF5ZWQgZm9yIHNpbXBsaWNpdHkgKGRlZmF1bHQpLCB3aGljaCByZWFsbHkgb25seSBhZmZlY3RzIEJQIHRoYXQgaGFzIDM3IHRlcm1zIHRvdGFsLgpFYWNoIG5vZGUgcmVwcmVzZW50cyBhIEdPIHRlcm0gYW5kIGJyYW5jaGVzIHJlcHJlc2VudCB0aGUgY29udGFpbm1lbnQgcmVsYXRpb25zaGlwcyBhbmQgdGhlIGRlZ3JlZSBvZiBjb2xvcnMgcmVwcmVzZW50IHRoZSBleHRlbnQgb2YgZW5yaWNobWVudCwgd2l0aCBibGFjayBhbmQgd2hpdGUgZWxsaXBzZXMgcmVwcmVzZW50aW5nIG5vbi1zaWduaWZpY2FudCBlbnJpY2htZW50IGFuZCB5ZWxsb3cgdG8gcmVkIHJlcHJlc2VudGluZyB0aGUgZ3JhZGllbnQgZnJvbSBoaWdoZXIgdG8gbG93ZXIgcC5hZGp1c3QgdmFsdWVzIChyZWQgbW9zdCBzaWduaWZpY2FudCkuIFRoZSBudW1iZXJzIHVuZGVyIHRoZSBHTyB0ZXJtcyBhcmUgdGhlIHJhdGlvIG9mIG1pc3NpbmcgYnVzY29zIGFubm90YXRlZCB3aXRoIHRoYXQgdGVybSBvdmVyIHRoZSBudW1iZXIgb2YgYnVzY29zIGFubm90YXRlZCB3aXRoIHRoYXQgdGVybSBpbiB0aGUgcmVmZXJlbmNlIGRhdGFiYXNlIChNaXNzaW5nIG92ZXIgYmFja2dyb3VuZCBjb2x1bW4gb2YgZW5yaWNoZWRfR08gZGYpLiBUb3AgMTAgb2Ygc2lnbmlmaWNhbnRseSBlbnJpY2hlZCB0ZXJtcyBhcmUgcmVwcmVzZW50ZWQgaW4gYm94ZXMgYW5kIHRoZSByZXN0IGluIGVsbGlwc2VzLiBCUDogQW1vbmcgdGhlIDM3IHNpZ25pZmljYW50bHkgZW5yaWNoZWQgR08gQlAgdGVybXMsIHRoZSBoaWdoZXIgbnVtYmVycyB3ZXJlIGluIGNpbGl1bSBhbmQgY2VsbCBwcm9qZWN0aW9uLgoKCiMjIFNjcmF0Y2gKVG8gcXVpY2tseSBsb29rIGF0IGEgR08gdGVybTogc2VhcmNoIGZvciBpZCBpbiBodHRwczovL3d3dy5lYmkuYWMudWsvUXVpY2tHTy8=